//promise 中状态分为等待（pending），
//成功（fulfilled），

const { reject } = require('lodash')

//失败（rejected）
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    // 创建一个执行者函数，用于初始创建
    executor(this.resolve, this.reject)
  }
  //设置状态，用于判断当前的promise执行的情况
  status = PENDING
  //成功执行后的返回值
  value = undefined
  //失败后的原因描述
  reason = undefined
  //存放成功回调
  successCallBack = []
  //存放失败回调
  failCallBack = []

  resolve = (value) => {
    //当状态不是等待 则状态不可以再更改，阻止向下继续执行
    if (this.status !== PENDING) return
    //修改状态为成功
    this.status = FULFILLED
    //保存成功的值
    this.value = value
    //如果有缓存的等待数据，一并执行
    while (this.successCallBack.length) {
      this.successCallBack.shift()() //出栈直接执行
    }
  }

  reject = (reason) => {
    //当状态不是等待 则状态不可以再更改，阻止向下继续执行
    if (this.status !== PENDING) return
    //修改状态为失败
    this.status = REJECTED
    //保存失败的原因
    this.reason = reason
    //如果有缓存的等待数据，一并执行
    while (this.failCallBack.length) {
      this.successCallBack.shift()() //出栈直接执行
    }
  }

  then(successFn, failFn) {
    // 用于then在无参的的时候处理
    successFn = successFn ? successFn : (value) => value
    failFn = failFn
      ? failFn
      : (reason) => {
          throw reason
        }

    //返回一个promise对象，用于then的链式调用
    return new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        try {
          this.resolveParam(successFn(this.value), resolve, reject)
        } catch (error) {
          reject(error)
        }
      } else if (this.status === REJECTED) {
        try {
          this.resolveParam(failFn(this.reason), resolve, reject)
        } catch (error) {
          reject(error)
        }
      } else {
        //当状态还处于等待状态时，需要先将数据缓存下来
        this.successCallBack.push(() => {
          try {
            this.resolveParam(successFn(this.value), resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
        this.failCallBack.push(() => {
          try {
            this.resolveParam(failFn(this.reason), resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
    })
  }
  //抽取的方法，处理执行参数
  resolveParam = (x, resolve, reject) => {
    //判断循环调用的情况，我直接判断this和返回值是否相等。就不用再调用setTimeout了
    if (x === this) {
      throw new Error('不允许循环调用同一个promise')
    }
    //判断x是promise对象还是普通值
    //如果是普通值，则直接执行成功处理
    //如果是promise对象，则继续让x判断执行resolve还是reject
    if (x instanceof MyPromise) {
      x.then(resolve, reject)
    } else {
      resolve(x)
    }
  }
  // 执行成功的静态方法
  static resolve(value) {
    //如果是promise对象，则直接返回
    //如果是普通对象，则转换成promise后返回
    if (value instanceof MyPromise) {
      return value
    } else {
      return new MyPromise((resolve) => resolve(value))
    }
  }
  // 静态方法，当所有传入的参数都执行成功后返回一个promis带有所有参数的执行结果的数组
  static all(array) {
    //装载执行完成的回调数据
    let finish = []
    return new MyPromise((resolve, reject) => {
      // 定义了一个内部函数，产生闭包，保存finish的数据不会因为在promise异步执行时丢失
      function setFinishItem(item) {
        finish.push(item)
        if (finish.length === array.length) {
          resolve(finish)
        }
      }
      array.forEach((item) => {
        //如果是promise对象时
        //否则就是普通值
        if (item instanceof MyPromise) {
          item.then(
            (v) => setFinishItem(v),
            (err) => reject(err)
          )
        } else {
          setFinishItem(item)
        }
      })
    })
  }
  // 静态方法，只要有个通过，就返回一个promise
  static race(array) {
    return new MyPromise((resolve, reject) => {
      array.forEach((item) => {
        //如果是promise对象时
        //否则就是普通值
        if (item instanceof MyPromise) {
          item.then(
            (v) => resolve(v),
            (err) => reject(err)
          )
        } else {
          resolve(item)
        }
      })
    })
  }
  // finall方法，虽然代表最终的意思，但是它任然有返回值，共then链式调用
  finally(callback){
    return this.then(value=>{
      return MyPromise.resolve(callback()).then(()=>value)
    },reason=>{
      return MyPromise.resolve(callback()).then(()=>{throw reason})
    })
  }

  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
}